/**
 *@description:
 *@file: yuv_video.cpp
 *@author: snow-tyan
 *@email: zziywang@163.com
 *@date: 2023-02-13 14:53:34
 *@copyright: 2018-2022 AiRiA. All rights reserved.
 */

#include <mygl/resource_manager.h>
#include <mygl/shader.h>
#include <mygl/static_renderer.h>
#include <mygl/texture2d.h>

#include <iostream>
#include <memory>
#include <vector>
#include <thread>

#include <GLFW/glfw3.h>

#ifndef PROJECT_ROOT
#define PROJECT_ROOT "/home/snow/OpenGLESProjects/MyOpenGLES_glfw"
#endif

using namespace mygl;

const std::string kProjectRoot = PROJECT_ROOT + std::string("/");
const std::string kResourcesRoot = kProjectRoot + "resources/videos/";
const std::string kShaderRoot = kProjectRoot + "07-InPractice/03-VideoRenderTest/yuv/";
const unsigned int kWindowWidth = 1280;
const unsigned int kWindowHeight = 720;
const char *kWindowName = "VideoRenderTestYuv";

void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);

int main() {
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

  GLFWwindow *window =
      glfwCreateWindow(kWindowWidth, kWindowHeight, kWindowName, nullptr, nullptr);
  if (!window) {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

  if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }
  // print: gl info
  printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR));  // 打印gl供应商
  printf("GL_VERSION: %s\n", glGetString(GL_VERSION));  // 打印gl版本，确定使用gles3
  int max_attributes;
  glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attributes);  // 硬件支持的最大顶点属性数量
  printf("Max number of vertex attribute supported: %d\n", max_attributes);

  // Shader
  ResourceManager::LoadShader("yuv2rgba", kShaderRoot + "shader.vs",
                              kShaderRoot + "shader.fs");
  // 590M
  ResourceManager::LoadYuvVideoTexture(
      "table_wb", kResourcesRoot + "table_wb_1280x720.nv21.yuv", 1280, 720);
  // 4.2G
  // ResourceManager::LoadYuvVideoTexture(
  //     "meeting", kResourcesRoot + "WIN_20221108_15_59_00_Pro_1920x1080.nv21.yuv", 1920,
  //     1080);
#if 0
  // Texture
  GLuint y_texture, uv_texture;
  glGenTextures(1, &y_texture);
  glBindTexture(GL_TEXTURE_2D, y_texture);
  // 先不加载图像格式
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glGenerateMipmap(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, GL_NONE);  // unbind

  glGenTextures(1, &uv_texture);
  glBindTexture(GL_TEXTURE_2D, uv_texture);
  glGenerateMipmap(GL_TEXTURE_2D);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  // Unbind texture
  glBindTexture(GL_TEXTURE_2D, GL_NONE);

  std::vector<std::vector<uint8_t>> y_datas, uv_datas;
  ReadYuv420pVideo(kResourcesRoot + "table_wb_1280x720.nv21.yuv", 1280, 720, y_datas, uv_datas);
#endif
  auto renderer = std::make_shared<StaticRenderer>(ResourceManager::GetShader("yuv2rgba"),
                                                   kWindowWidth, kWindowHeight);
  // render loop
  // -----------
  auto s = TimeNow(), e = TimeNow();
  size_t frame = 0;
  float fps = 30.0f;
  auto timer = TimeNow();
  while (!glfwWindowShouldClose(window)) {
    processInput(window);
    // 添加定时器
    timer += std::chrono::milliseconds(static_cast<int>(1000 / fps));
    std::this_thread::sleep_until(timer);
    // render
    // ------
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

// render container
#if 0
    renderer->RenderYuvVideo(y_texture, uv_texture, 1280, 720, y_datas[frame], uv_datas[frame],
                             ImgFmt::YUV420SP_NV21, glm::vec2(0.0f, 0.0f),
                             glm::vec2(kWindowWidth, kWindowHeight));
#endif
    renderer->RenderYuvVideo(ResourceManager::GetYuvTexture("table_wb"), frame,
                             ImageFormat::YUV420SP_NV21, glm::vec2(0.0f, 0.0f),
                             glm::vec2(kWindowWidth, kWindowHeight));
    glfwSwapBuffers(window);
    glfwPollEvents();
    if (frame % 30 == 0) {
      e = TimeNow();
      printf("render 30 frames cost time=%lf\n", CalcPerf(s, e));
      s = TimeNow();
    }
    ++frame;
  }
  ResourceManager::Clear();
  glfwTerminate();
  return 0;
}

void processInput(GLFWwindow *window) {
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
    glfwSetWindowShouldClose(window, true);
  }
}
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
  glViewport(0, 0, width, height);
}